﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;

namespace Nate_sRoguelikeGame
{
    class Level
    {
        private MapBlock[][] map;
        public Rectangle Dimensions { get; private set; }
        public GameArchitecture BaseArchitecture { get; private set; }
        public int Depth { get; private set; }
        public List<MapBlock> ChangedBlocks { get; private set; }
        public Level LevelAbove { get; private set; }
        public Level LevelBelow { get; private set; }
        public int[] RoomCountsBySize = new int[] { 0, 0, 0 };

        public Level(string name, Rectangle dimensions, GameArchitecture baseArchitecture)
        {
            Dimensions = dimensions;
            map = new MapBlock[dimensions.Width][];
            Depth = 0;
            ChangedBlocks = new List<MapBlock>();
            BaseArchitecture = baseArchitecture;

            // Generate level full of baseArchitecture
            for (int x = 0; x < Dimensions.Width; x++)
            {
                var line = new MapBlock[Dimensions.Height];
                for (int y = 0; y < Dimensions.Height; y++)
                    line[y] = new MapBlock(this, new Point(x, y)) { Architecture = baseArchitecture };
                map[x] = line;
            }


        }

        public void Draw()
        {
            foreach (MapBlock block in ChangedBlocks)
                block.Draw();
            ChangedBlocks.Clear();
        }

        public MapBlock GetMapBlockAt(Point p)
        {
            return (p.X >= map.Length || p.X < 0 || p.Y >= map[0].Length || p.Y < 0)
                       ? null
                       : map[p.X][p.Y];
        }

        public IEnumerable<MapBlock> GetSurroundingPassableMapBlocks(Player player)
        {
            return GetSurroundingPassableMapBlocks(player, true);
        }

        public IEnumerable<MapBlock> GetSurroundingPassableMapBlocks(Player player, bool includeDiagonals)
        {
            return GetSurroundingMapBlocks(player.MapBlock.Location, includeDiagonals).Where(b => b.IsPassableBy(player));
        }

        public IEnumerable<MapBlock> GetSurroundingMapBlocks(Point location)
        {
            return GetSurroundingMapBlocks(location, true);
        }

        public IEnumerable<MapBlock> GetSurroundingMapBlocks(Point location, bool includeDiagonals)
        {

            List<MapBlock> blocks = new List<MapBlock>
                                        {
                                            // East, West, South, North
                                            map[Math.Min(map.Length - 1, location.X + 1)][location.Y],
                                            map[Math.Max(0, location.X - 1)][location.Y],
                                            map[location.X][Math.Min(map[0].Length - 1, location.Y + 1)],
                                            map[location.X][Math.Max(0, location.Y - 1)]
                                        };
            // Diagonals (Northwest, Southwest, Northeast, Southeast)
            if (includeDiagonals)
            {
                blocks.Add(map[Math.Max(0, location.X - 1)][Math.Max(0, location.Y - 1)]);
                blocks.Add(map[Math.Max(0, location.X - 1)][Math.Min(map[0].Length - 1, location.Y + 1)]);
                blocks.Add(map[Math.Min(map.Length - 1, location.X + 1)][Math.Max(0, location.Y - 1)]);
                blocks.Add(map[Math.Min(map.Length - 1, location.X + 1)][Math.Min(map[0].Length - 1, location.Y + 1)]);
            }
            // Cycle through all the surrounding blocks
            for (int x1 = Math.Max(0, location.X - 1); x1 <= Math.Min(map.Length - 1, location.X + 1); x1++)
            {
                for (int y1 = Math.Max(0, location.Y - 1); y1 <= Math.Min(map[0].Length - 1, location.Y + 1); y1++)
                {
                    // Don't add the current block, only the surrounding ones
                    if (x1 == location.X && y1 == location.Y)
                    {
                        continue;
                    }
                    blocks.Add(map[x1][y1]);
                }
            }
            return blocks;
        }

        public static Level GenerateLevel(string name, Rectangle dimensions, GameArchitecture initialBaseArchitecture, GameArchitecture baseArchitecture, GameArchitecture wallArchitecture, bool withStairsUp, bool withStairsDown, Level lastLevel)
        {
            var level = new Level(name, dimensions, initialBaseArchitecture);
            level.BaseArchitecture = baseArchitecture;
            Random random = new Random();

            int numberOfRooms = (Game.Roll(14, 18) + 48) / 10;
            int minRoomSize = 4;
            Point maxRoomSize = new Point(dimensions.Width - (4 * (Math.Min(numberOfRooms, 2) - 1)),
                                          dimensions.Height - (2 * (Math.Min(numberOfRooms, 2) - 1)));
            if (File.Exists("map.txt"))
                File.Delete("map.txt");
            List<Room> rooms = new List<Room>();
            int remainingX = dimensions.Width, remainingY = dimensions.Height, remainingArea = dimensions.GetArea();
            int maxArea = (int)(remainingArea * 0.667);
            for (int i = 0; i < numberOfRooms; i++)
            {
                var room = new Room(level, i.ToString(), wallArchitecture, rooms, dimensions);

                if (room.Place() && room.Resize())
                {
                    rooms.Add(room);
                    room.AddToMap(level.map);
                }
            }

            // Position player inside a room
            var roll = Game.Roll(1, rooms.Count, true);
            var selectedRoomPerimeter = rooms[roll].InnerPerimeter;
            var loc = new Point(selectedRoomPerimeter.X + Game.Roll(1, selectedRoomPerimeter.Width, true),
                                selectedRoomPerimeter.Y + Game.Roll(1, selectedRoomPerimeter.Height, true));
            Game.Player.MapBlock =
                level.GetMapBlockAt(loc);
            if (Game.Player.LastAction == GameAction.Ascend)
            {
                level.LevelBelow = lastLevel;
                Game.Player.MapBlock.Architecture = GameArchitecture.StairsDown;
            }
            else if (Game.Player.LastAction == GameAction.Descend)
            {
                level.LevelAbove = lastLevel;
                Game.Player.MapBlock.Architecture = GameArchitecture.StairsUp;
            }

            return level;
        }

    }
}
